package club.ruanx.auth.vcode.filter;

import club.ruanx.auth.vcode.enumeration.ValidateCodeType;
import club.ruanx.auth.vcode.exception.ValidateCodeException;
import club.ruanx.auth.vcode.factory.ValidateCodeProcessorManager;
import club.ruanx.security.service.SysRedisService;
import club.ruanx.security.util.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author 阮胜
 * @date 2018/9/30 17:38
 */
@Slf4j
@Component
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
    private static final String HTTP_GET = "get";
    private static final String VCODE = "vcode";
    private static final int MAX_LOGIN_FAILURE_COUNT = 3;
    private static final String USERNAME = "username";
    private static final String USER_LOGIN = "/user/login";
    /**
     * 存放所有需要校验验证码的url
     */
    private Map<String, ValidateCodeType> urlMap = new HashMap<>();
    /**
     * 验证请求url与配置的url是否匹配的工具类
     */
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    @Autowired
    private ValidateCodeProcessorManager validateCodeProcessorManager;

    @Autowired
    private SysRedisService sysRedisService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        ValidateCodeType validateCodeType = getValidateCodeType(request);
        String username = request.getParameter(USERNAME);
        if (validateCodeType != null && isNeedToValidate(username)) {
            log.debug("校验请求(" + request.getRequestURI() + ")中的验证码,验证码类型" + validateCodeType);
            try {
                String vcode = obtainVCode(request);
                //根据验证码类型校验验证码
                validateCodeProcessorManager.findValidateCodeProcessor(validateCodeType)
                        .validate(vcode, validateCodeType);
            } catch (ValidateCodeException e) {
                ResponseUtils.sendMessage(response, false, e.getMessage(), 900);
                return;
            }
        }

        chain.doFilter(request, response);
    }

    private String obtainVCode(HttpServletRequest request) {
        return request.getParameter(VCODE);
    }

    /**
     * 是否需要验证
     *
     * @param username
     * @return
     */
    private boolean isNeedToValidate(String username) {
        int userLoginFailureCounter = sysRedisService.getUserLoginFailureCounter(username);
        return userLoginFailureCounter >= MAX_LOGIN_FAILURE_COUNT;
    }

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();

        urlMap.put(USER_LOGIN, ValidateCodeType.IMAGE);
    }

    /**
     * 获取校验码的类型，如果当前请求不需要校验，则返回null
     *
     * @param request
     * @return
     */
    private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
        ValidateCodeType result = null;
        if (!StringUtils.equalsIgnoreCase(request.getMethod(), HTTP_GET)) {
            Set<String> urls = urlMap.keySet();
            for (String url : urls) {
                if (pathMatcher.match(url, request.getRequestURI())) {
                    result = urlMap.get(url);
                }
            }
        }
        return result;
    }
}
